package com.nulldev.util.io;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.FileNameMap;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import com.nulldev.util.crypto.api2.EncryptionUtil;
import com.nulldev.util.crypto.api2.EncryptionUtil.Types;
import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.java.ReflectionUtils;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.math.MathUtil;
import com.nulldev.util.math.lNumber;
import com.nulldev.util.os.OSUtil;
import com.nulldev.util.os.OSUtil.OSType;
import com.nulldev.util.other.ArrayUtils;
import com.nulldev.util.other.ByteUtils;

public class FileUtil {

	@Deprecated
	public static enum FileType {
		
		IMAGE("png", "jpeg", "jpg", "svg", "bpg"),
		ANIMATED_IMAGE("gif"),
		VIDEO("mp4", "avi", "h264", "webm", "mkv"),
		Music("mp3", "ogg", "wav", "pcmec", "pcme", "aac", "flac")
		
		;
		
		private final String[] fE;
		private FileType(final String... fileExtensions) {
			this.fE = fileExtensions;
		}
		
		public String[] getExtensions() {
			return this.fE;
		}
		
		public boolean isType(final String type) {
			return ArrayUtils.contains(this.fE, type);
		}
	}
	
	public static ArrayList<String> readFile(final File file) throws Exception{
		final ArrayList<String> data = new ArrayList<String>();
		if (file.isDirectory()) {
			throw new Exception("File is a directory.");
		}
		final InputStream fis = new FileInputStream(file);
		final InputStreamReader isr = new InputStreamReader(fis, Charset.forName("UTF-8"));
		final BufferedReader br = new BufferedReader(isr);
		String buffer;
		while ((buffer = br.readLine()) != null){
			data.add(buffer);
		}
		try {
			fis.close();
			isr.close();
			br.close();
		}catch (Exception e) {}
		return data;
	}
	
	public static String readFirstLine(final File file) throws Exception{
		String data = "";
		if (file.isDirectory()) {
			throw new Exception("File is a directory.");
		}
		final InputStream fis = new FileInputStream(file);
		final InputStreamReader isr = new InputStreamReader(fis, Charset.forName("UTF-8"));
		final BufferedReader br = new BufferedReader(isr);
		String buffer;
		while ((buffer = br.readLine()) != null){
			data = buffer;
			break;
		}
		try {
			fis.close();
			isr.close();
			br.close();
		}catch (Exception e) {}
		return data;
	}
	
	public static ArrayList<String> readFile(final File file, final Charset charset) throws Exception{
		final ArrayList<String> data = new ArrayList<String>();
		if (file.isDirectory()) {
			throw new Exception("File is a directory.");
		}
		final InputStream fis = new FileInputStream(file);
		final InputStreamReader isr = new InputStreamReader(fis, charset);
		final BufferedReader br = new BufferedReader(isr);
		String buffer;
		while ((buffer = br.readLine()) != null){
			data.add(buffer);
		}
		try {
			fis.close();
			isr.close();
			br.close();
		}catch (Exception e) {}
		return data;
	}
	
	public static byte[] getResourceBytes(final Class<?> cls, final String resourcePath) {
		try {
	        InputStream in = cls.getResourceAsStream(resourcePath);
	        if (in == null) {
	        	LoggerFactory.getLogger(true, true).error("getResourceBytes(file) -> File not found! Returning null!");
	            return null;
	        }

	        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
	        
	        byte[] buffer = new byte[1024];
	        int bytesRead;
	        while ((bytesRead = in.read(buffer)) != -1) {
	        	baos.write(buffer, 0, bytesRead);
	        }
	        final byte[] out = baos.toByteArray();
	        baos.close();
	        
	        return out;
	    } catch (Exception e) {
	    	LoggerFactory.getLogger(true, true).error("getResourceBytes(file) -> Failed to read resource!", e);
	        return null;
	    }
	}
	
	public static InputStream getResourceStream(final Class<?> cls, final String resourcePath) {
		try {
	        InputStream in = cls.getResourceAsStream(resourcePath);
	        if (in == null) {
	        	LoggerFactory.getLogger().error("getResourceBytes(file) -> File not found! Returning null!");
	            return null;
	        }

	        return in;
	    } catch (Exception e) {
	    	LoggerFactory.getLogger().error("getResourceBytes(file) -> Failed to read resource!", e);
	        return null;
	    }
	}
	
	public static String getResourceAsString(final Class<?> cls, final String resourcePath) {
		try {
	        return new String(getResourceBytes(cls, resourcePath));
	    } catch (Exception e) {
	    	LoggerFactory.getLogger().error("getResourceAsString(file) -> Failed to read resource!", e);
	        return null;
	    }
	}

	public static File getResourceAsFile(final Class<?> cls, final String resourcePath) {
	    try {
	        InputStream in = cls.getResourceAsStream(resourcePath);
	        if (in == null) {
	        	LoggerFactory.getLogger().error("getResourceAsFile(file) -> File not found! Returning null!");
	            return null;
	        }

	        File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
	        tempFile.deleteOnExit();

	        final FileOutputStream out = new FileOutputStream(tempFile);
	        byte[] buffer = new byte[1024];
	        int bytesRead;
	        while ((bytesRead = in.read(buffer)) != -1) {
	            out.write(buffer, 0, bytesRead);
	        }
	        out.close();

	        return tempFile;
	    } catch (IOException e) {
	    	LoggerFactory.getLogger().error("getResourceAsFile(file) -> Failed to read resource!", e);
	        return null;
	    }
	}

	public static void hide(final File src) throws Exception {
	    if (OSUtil.getOperatingSystemType() != OSType.Windows) {
	    	return;
	    }
	    final Process p = Runtime.getRuntime().exec("attrib +h " + src.getPath());
	    p.waitFor();
	}

	public static void writeFile(final File file, final String data) throws Exception{
		if (file.isDirectory()) {
			throw new Exception("File is a directory.");
		}
		if (!file.exists()) {
			file.createNewFile();
		}
	    final PrintWriter writer = new PrintWriter(file, "UTF-8");
	    writer.println(data);
	    writer.close();
	}

	public static void writeFile(final File file, final String data, boolean println) throws Exception{
		if (file.isDirectory()) {
			throw new Exception("File is a directory.");
		}
	    final PrintWriter writer = new PrintWriter(file, "UTF-8");
	    if (println) {
	    	writer.println(data);
	    }else {
	    	writer.print(data);
	    }
	    writer.close();
	}
	
	public static void append(final File file, final String data, final boolean println) throws Exception{
		if (file.isDirectory()) {
			throw new Exception("File is a directory.");
		}
	    final FileWriter writer = new FileWriter(file, true);
	    if (println) {
	    	writer.append(data + JVMUtil.LINE_SEP_BASIC);
	    }else {
	    	writer.append(data);
	    }
	    writer.close();
	}


	public static void writeFile(final File file, final List<String> data) throws Exception{
		if (file.isDirectory()) {
			throw new Exception("File is a directory.");
		}
	    final PrintWriter writer = new PrintWriter(file, "UTF-8");
	    for (final String dta : data) {
	    	writer.println(dta);
	    }
	    writer.close();
	}
	
	public static void writeFile(final File file, 
			final List<String> data, final boolean addNewLine) throws Exception{
		if (file.isDirectory()) {
			throw new Exception("File is a directory.");
		}
	    final PrintWriter writer = new PrintWriter(file, "UTF-8");
	    for (final String dta : data) {
	    	writer.println(dta + (addNewLine ? JVMUtil.LINE_SEP_BASIC : ""));
	    }
	    writer.close();
	}

	public static ArrayList<File> listFilesForFolder(final File folder) {
		if (folder == null) {
			LoggerFactory.getLogger().error("listFilesForFolder(folder) -> Input was null!");
			return new ArrayList<File>();
		}
		final ArrayList<File> files = new ArrayList<File>();
		if (folder.listFiles() == null) {
			LoggerFactory.getLogger().error("listFilesForFolder(folder) -> Input is a file!");
			return new ArrayList<File>();
		}
	    for (final File fileEntry : folder.listFiles()) {
	        if (fileEntry.isDirectory()) {
	            files.addAll(listFilesForFolder(fileEntry));
	            files.add(fileEntry);
	        } else {
	            files.add(fileEntry);
	        }
	    }
	    return files;
	}
	
	public static ArrayList<File> listFilesForFolderExt(final File folder, final String extension) {
		if (folder == null) {
			LoggerFactory.getLogger().error("listFilesForFolder(folder) -> Input was null!");
			return new ArrayList<File>();
		}
		final ArrayList<File> files = new ArrayList<File>();
		if (folder.listFiles() == null) {
			LoggerFactory.getLogger().error("listFilesForFolder(folder) -> Input is a file!");
			return new ArrayList<File>();
		}
	    for (final File fileEntry : folder.listFiles()) {
	        if (fileEntry.isDirectory()) {
	            files.addAll(listFilesForFolderExt(fileEntry, extension));
	            if (getFileExtension(fileEntry).equals(extension)) {
	            	files.add(fileEntry);
	            }
	        } else {
	            if (getFileExtension(fileEntry).equals(extension)) {
	            	files.add(fileEntry);
	            }
	        }
	    }
	    return files;
	}
	
	public static ArrayList<File> listFilesForFolderNR(final File folder) {
		if (folder == null) {
			LoggerFactory.getLogger().error("listFilesForFolder(folder) -> Input was null!");
			return new ArrayList<File>();
		}
		final ArrayList<File> files = new ArrayList<File>();
	    for (final File fileEntry : folder.listFiles()) {
	        if (fileEntry.isDirectory()) {
	            files.add(fileEntry);
	        } else {
	            files.add(fileEntry);
	        }
	    }
	    return files;
	}

	public static boolean deleteDir(File dir) {
	    if (dir.isDirectory()) {
	        String[] children = dir.list();
	        for (int i = 0; i < children.length; i++) {
	            boolean success = deleteDir(new File(dir, children[i]));
	            if (!success) {
	                return false;
	            }
	        }
	    }

	    return dir.delete();
	}

	private static final long K = 1024;
	private static final long M = K * K;
	private static final long G = M * K;
	private static final long T = G * K;
	private static final long P = T * K;

	public static long getSize(final File file) {
        long size = 0;

        if (file.isDirectory()) {
               File[] files = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                   size += getSize(files[i]);
            }
        }
        else {
               size += file.length();
        }

        return size;
   }

   public static String convertToStringRepresentation(final long value){
	    final long[] dividers = new long[] {P, T, G, M, K, 1 };
	    final String[] units = new String[] {"PB", "TB", "GB", "MB", "KB", "B" };
	    if(value < 0) {
	    	return "N/A";
	    }
	        //throw new IllegalArgumentException("Invalid file size: " + value);
	    String result = null;
	    for(int i = 0; i < dividers.length; i++){
	        final long divider = dividers[i];
	        if(value >= divider){
	            result = format(value, divider, units[i]);
	            break;
	        }
	    }
	    return result;
   }

   private static final DecimalFormat frmt = new DecimalFormat("#,##0.##");
   public static final String HOME_DIRECTORY = System.getProperty("user.home");
   private static String format(final long value, final long divider, final String unit){
	    final double result =
	        divider > 1 ? (double) value / (double) divider : (double) value;
	    return frmt.format(result) + " " + unit;
   }

   public static void copyDirectoryToDirectory(final File srcDir, final File destDir) throws IOException {
       if (srcDir == null) {
           throw new NullPointerException("Source must not be null");
       }
       if (srcDir.exists() && !srcDir.isDirectory()) {
           throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
       }
       if (destDir == null) {
           throw new NullPointerException("Destination must not be null");
       }
       if (destDir.exists() && !destDir.isDirectory()) {
           throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
       }
       copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
   }

   public static void copyDirectory(final File srcDir, final File destDir) throws IOException {
       copyDirectory(srcDir, destDir, true);
   }

   public static void copyDirectoryDebug(final File srcDir, final File destDir) throws IOException {
       copyDirectoryDebug(srcDir, destDir, true);
   }

   public static void copyDirectory(final File srcDir, final File destDir, final boolean preserveFileDate) throws IOException {
       copyDirectory(srcDir, destDir, null, preserveFileDate);
   }

   public static void copyDirectoryDebug(final File srcDir, final File destDir, final boolean preserveFileDate) throws IOException {
       copyDirectoryDebug(srcDir, destDir, null, preserveFileDate);
   }

   public static void copyDirectory(final File srcDir, final File destDir, final FileFilter filter) throws IOException {
       copyDirectory(srcDir, destDir, filter, true);
   }

   public static void copyDirectoryDebug(final File srcDir, final File destDir, final FileFilter filter) throws IOException {
       copyDirectoryDebug(srcDir, destDir, filter, true);
   }

   public static void copyDirectory(final File srcDir, final File destDir, final FileFilter filter, final boolean preserveFileDate) throws IOException {
       if (srcDir == null) {
           throw new NullPointerException("Source must not be null");
       }
       if (destDir == null) {
           throw new NullPointerException("Destination must not be null");
       }
       if (!srcDir.exists()) {
           throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
       }
       if (!srcDir.isDirectory()) {
           throw new IOException("Source '" + srcDir + "' exists but is not a directory");
       }
       if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
           throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
       }
       List<String> exclusionList = null;
       if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
           final File[] srcFiles = (filter == null) ? srcDir.listFiles() : srcDir.listFiles(filter);
           if (srcFiles != null && srcFiles.length > 0) {
               exclusionList = new ArrayList<String>(srcFiles.length);
               for (final File srcFile : srcFiles) {
                   final File copiedFile = new File(destDir, srcFile.getName());
                   exclusionList.add(copiedFile.getCanonicalPath());
               }
           }
       }
       doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
   }

   public static void copyDirectoryDebug(final File srcDir, final File destDir, final FileFilter filter, final boolean preserveFileDate) throws IOException {
       if (srcDir == null) {
           throw new NullPointerException("Source must not be null");
       }
       if (destDir == null) {
           throw new NullPointerException("Destination must not be null");
       }
       if (!srcDir.exists()) {
           throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
       }
       if (!srcDir.isDirectory()) {
           throw new IOException("Source '" + srcDir + "' exists but is not a directory");
       }
       if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
           throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
       }
       List<String> exclusionList = null;
       if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
    	   int i = 0;
           final File[] srcFiles = (filter == null) ? srcDir.listFiles() : srcDir.listFiles(filter);
           if (srcFiles != null && srcFiles.length > 0) {
               exclusionList = new ArrayList<String>(srcFiles.length);
               for (final File srcFile : srcFiles) {
            	   i += 1;
            	   System.out.println("[FileUtil] Copying file... (" + i + " / " + srcFiles.length + ")");
                   final File copiedFile = new File(destDir, srcFile.getName());
                   exclusionList.add(copiedFile.getCanonicalPath());
               }
           }
       }
       doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
   }

   private static void doCopyDirectory(final File srcDir, final File destDir, final FileFilter filter, final boolean preserveFileDate, final List<String> exclusionList) throws IOException {
       final File[] srcFiles = (filter == null) ? srcDir.listFiles() : srcDir.listFiles(filter);
       if (srcFiles == null) {
           throw new IOException("Failed to list contents of " + srcDir);
       }
       if (destDir.exists()) {
           if (!destDir.isDirectory()) {
               throw new IOException("Destination '" + destDir + "' exists but is not a directory");
           }
       }
       else if (!destDir.mkdirs() && !destDir.isDirectory()) {
           throw new IOException("Destination '" + destDir + "' directory cannot be created");
       }
       if (!destDir.canWrite()) {
           throw new IOException("Destination '" + destDir + "' cannot be written to");
       }
       for (final File srcFile : srcFiles) {
           final File dstFile = new File(destDir, srcFile.getName());
           if (exclusionList == null || !exclusionList.contains(srcFile.getCanonicalPath())) {
               if (srcFile.isDirectory()) {
                   doCopyDirectory(srcFile, dstFile, filter, preserveFileDate, exclusionList);
               }
               else {
                   doCopyFile(srcFile, dstFile, preserveFileDate);
               }
           }
       }
       if (preserveFileDate) {
           destDir.setLastModified(srcDir.lastModified());
       }
   }

   public static void copyFile(final File srcFile, final File destFile) throws IOException {
       copyFile(srcFile, destFile, true);
   }

   public static void copyFile(final File srcFile, final File destFile, final boolean preserveFileDate) throws IOException {
       if (srcFile == null) {
           throw new NullPointerException("Source must not be null");
       }
       if (destFile == null) {
           throw new NullPointerException("Destination must not be null");
       }
       if (!srcFile.exists()) {
           throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
       }
       if (srcFile.isDirectory()) {
           throw new IOException("Source '" + srcFile + "' exists but is a directory");
       }
       if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
           throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
       }
       final File parentFile = destFile.getParentFile();
       if (parentFile != null && !parentFile.mkdirs() && !parentFile.isDirectory()) {
           throw new IOException("Destination '" + parentFile + "' directory cannot be created");
       }
       if (destFile.exists() && !destFile.canWrite()) {
           throw new IOException("Destination '" + destFile + "' exists but is read-only");
       }
       doCopyFile(srcFile, destFile, preserveFileDate);
   }

   public static long copyFile(final File input, final OutputStream output) throws IOException {
       final FileInputStream fis = new FileInputStream(input);
       try {
           return IOUtils.copyLarge(fis, output);
       }
       finally {
           fis.close();
       }
   }

   public static void doCopyFile(final File srcFile, final File destFile, final boolean preserveFileDate) throws IOException {
       if (destFile.exists() && destFile.isDirectory()) {
           throw new IOException("Destination '" + destFile + "' exists but is a directory");
       }
       FileInputStream fis = null;
       FileOutputStream fos = null;
       FileChannel input = null;
       FileChannel output = null;
       try {
           fis = new FileInputStream(srcFile);
           fos = new FileOutputStream(destFile);
           input = fis.getChannel();
           output = fos.getChannel();
           for (long size = input.size(), pos = 0L, count = 0L; pos < size; pos += output.transferFrom(input, pos, count)) {
               count = ((size - pos > 31457280L) ? 31457280L : (size - pos));
           }
       }
       finally {
           IOUtils.closeQuietly(output);
           IOUtils.closeQuietly(fos);
           IOUtils.closeQuietly(input);
           IOUtils.closeQuietly(fis);
       }
       if (srcFile.length() != destFile.length()) {
           throw new IOException("Failed to copy full contents from '" + srcFile + "' to '" + destFile + "'");
       }
       if (preserveFileDate) {
           destFile.setLastModified(srcFile.lastModified());
       }
   }

	 public static String getFileExtension(File file) {
	     String name = file.getName();
	     try {
	         return name.substring(name.lastIndexOf(".") + 1);
	     } catch (Exception e) {
	         return "";
	     }
	 }

	 public static long sizeOf(final File file) {
	        if (!file.exists()) {
	            final String message = file + " does not exist";
	            throw new IllegalArgumentException(message);
	        }
	        if (file.isDirectory()) {
	            return sizeOfDirectory0(file);
	        }
	      return file.length();
	 }
	 
	 private static long sizeOfDirectory0(final File directory) {
	        final File[] files = directory.listFiles();
	        if (files == null) {
	            return 0L;
	        }
	        long size = 0L;
	        for (final File file : files) {
	            try {
	                if (!isSymlink(file)) {
	                    size += sizeOf0(file);
	                    if (size < 0L) {
	                        break;
	                    }
	                }
	            }
	            catch (IOException ex) {}
	        }
	        return size;
	  }

	  private static long sizeOf0(final File file) {
	        if (file.isDirectory()) {
	            return sizeOfDirectory0(file);
	        }
	        return file.length();
	  }
	    
	    private static final Logger log = LoggerFactory.getLogger(true, true);

	    public static boolean isSymlink(final File file) throws IOException {
	        if (file == null) {
	            throw new NullPointerException("File must not be null");
	        }
	        if (JVMUtil.getJavaVer_Double() < 1.7) {
	        	//System.err.println("[Error @ FileUtil.isSymlink] Java version is unsupported, Minimum: 1.7 or higher.");
	        	log.error("isSymlink(file) -> Java JRE version is unsupported. Requries at least 1.7.");
	        	return false;//throw new UnsupportedOperationException("Java needs to be 1.7 or higher.");
	        }
	        return (Boolean) ReflectionUtils.invokeFunction(ReflectionUtils.fetchClass("java.nio.file.Files"), "isSymbolicLink", ReflectionUtils.invokeFunction(file, "toPath", null)); //Files.isSymbolicLink(file.toPath());
	    }

	    public static boolean deleteQuietly(final File file) {
	        if (file == null) {
	            return false;
	        }
	        try {
	            if (file.isDirectory()) {
	                cleanDirectory(file);
	            }
	        }
	        catch (Exception ex) {}
	        try {
	            return file.delete();
	        }
	        catch (Exception ignored) {
	            return false;
	        }
	    }

	    public static void cleanDirectory(final File directory) throws IOException {
	        final File[] files = verifiedListFiles(directory);
	        IOException exception = null;
	        for (final File file : files) {
	            try {
	                forceDelete(file);
	            }
	            catch (IOException ioe) {
	            	ioe.printStackTrace();
	                exception = ioe;
	            }
	        }
	        if (null != exception) {
	            throw exception;
	        }
	    }

	    private static File[] verifiedListFiles(final File directory) throws IOException {
	        if (!directory.exists()) {
	            final String message = directory + " does not exist";
	            throw new IllegalArgumentException(message);
	        }
	        if (!directory.isDirectory()) {
	            final String message = directory + " is not a directory";
	            throw new IllegalArgumentException(message);
	        }
	        final File[] files = directory.listFiles();
	        if (files == null) {
	            throw new IOException("Failed to list contents of " + directory);
	        }
	        return files;
	    }

	    public static void forceDelete(final File file) throws IOException {
	        if (file.isDirectory()) {
	            deleteDirectory(file);
	        }
	        else {
	            final boolean filePresent = file.exists();
	            if (!file.delete()) {
	                if (!filePresent) {
	                    //throw new FileNotFoundException("File does not exist: " + file);
	                }
	                final String message = "[Error @ FileUtil.forceDelete] Unable to delete file: " + file;
	                System.err.println(message);
	                //throw new IOException(message);
	            }
	        }
	    }

	    public static void deleteDirectory(final File directory) throws IOException {
	        if (!directory.exists()) {
	            return;
	        }
	        if (!isSymlink(directory)) {
	            cleanDirectory(directory);
	        }
	        if (!directory.delete()) {
	            final String message = "[Error @ FileUtil.deleteDirectory] Unable to delete directory " + directory + ".";
	            System.err.println(message);
	            //throw new IOException(message);
	        }
	    }

	    public static long copy(final InputStream input, final OutputStream output, final int bufferSize) throws IOException {
	        return copyLarge(input, output, new byte[bufferSize]);
	    }

	    public static long copyLarge(final InputStream input, final OutputStream output) throws IOException {
	        return copy(input, output, 4096);
	    }

	    public static long copyLarge(final InputStream input, final OutputStream output, final byte[] buffer) throws IOException {
	        long count = 0L;
	        int n;
	        while (-1 != (n = input.read(buffer))) {
	            output.write(buffer, 0, n);
	            count += n;
	        }
	        return count;
	    }

	    public static int copy(final InputStream input, final OutputStream output) throws IOException {
	        final long count = copyLarge(input, output);
	        if (count > 2147483647L) {
	            return -1;
	        }
	        return (int)count;
	    }

	    public static byte[] toByteArray(final InputStream input) throws IOException {
	        try {
	        	final ByteArrayOutputStream output = new ByteArrayOutputStream();
	            copy(input, output);
	            return output.toByteArray();
	        }catch (Exception e) {
	        	throw new RuntimeException(e);
	        }
	    }

	    public static byte[] toByteArray(final InputStream input, final long size) throws IOException {
	        if (size > 2147483647L) {
	            throw new IllegalArgumentException("Size cannot be greater than Integer max value: " + size);
	        }
	        return toByteArray(input, (int) size);
	    }

	    public static byte[] toByteArray(final InputStream input, final int size) throws IOException {
	        if (size < 0) {
	            throw new IllegalArgumentException("Size must be equal or greater than zero: " + size);
	        }
	        if (size == 0) {
	            return new byte[0];
	        }
	        byte[] data;
	        int offset;
	        int read;
	        for (data = new byte[size], offset = 0; offset < size && (read = input.read(data, offset, size - offset)) != -1; offset += read) {}
	        if (offset != size) {
	            throw new IOException("Unexpected read size. current: " + offset + ", expected: " + size);
	        }
	        return data;
	    }

		public static ByteBuffer resourceToByteBuffer(final String path, final Class<?> c) throws IOException {
			final byte[] data = toByteArray(c.getResourceAsStream(path));
			return ByteBuffer.wrap(data);
		}

		public static String read(final InputStream stream) throws Exception{
			final ByteArrayOutputStream result = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int length;
			while ((length = stream.read(buffer)) != -1) {
			    result.write(buffer, 0, length);
			}
			return result.toString("UTF-8");
		}

		public static void write(final File file, final File folder) throws IOException {
			if (!folder.isDirectory()) {
				throw new RuntimeException("Folder Variable is not a folder!");
			}
			final File out = new File(folder.getPath() + "/" +  file.getName());
			final InputStream in = new FileInputStream(file);
			if (in == null) {
				throw new RuntimeException("Input stream is null!");
			}
	        final FileOutputStream f = new FileOutputStream(out);
	        byte[] buffer = new byte[1024];
	        int bytesRead;
	        while ((bytesRead = in.read(buffer)) != -1) {
	            f.write(buffer, 0, bytesRead);
	        }
	        f.close();
	        in.close();
		}

		public static File safeCreate(final String location) {
			try {
				return new File(location);
			}catch (Exception e) {
				e.printStackTrace();
				return null;
			}			
		}
		
		public static File safeCreate(final URL location) {
			try {
				return new File(location.toURI());
			}catch (Exception e) {
				e.printStackTrace();
				try{
					return new File(HOME_DIRECTORY + "/nutest.txt");
				}catch (Exception e1) {
					return null;
				}
			}			
		}

		public static String readFileToString(final File file)  {
			try {
				final ArrayList<String> lines = readFile(file);
				final StringBuilder builder = new StringBuilder();
				for (int i = 0; i < lines.size(); i++) {
					final String str = lines.get(i);
					if (i == lines.size() - 1) {
						builder.append(str);
					}else {
						builder.append(str + "\n");
					}
				}
				
				return builder.toString();
			}catch (Exception e) {
				e.printStackTrace();
				return "readFileToString.Error";
			}
		}
		
		public static byte[] read(File file) throws IOException, Exception {
//		    if (file.length() > MAX_FILE_SIZE) {
//		        throw new Exception(file);
//		    }

		    byte[] buffer = new byte[(int) file.length()];
		    InputStream ios = null;
		    try {
		        ios = new FileInputStream(file);
		        if (ios.read(buffer) == -1) {
		            throw new IOException(
		                    "EOF reached while trying to read the whole file");
		        }
		    } finally {
		        try {
		            if (ios != null)
		                ios.close();
		        } catch (IOException e) {
		        }
		    }
		    return buffer;
		}
		
		public static enum ReadFileBufferState {
			OK, FAILED, ERROR, FINISHED;
		}
		
		public static interface ReadFileBufferCallback {
			public void read(final byte[] data, final ReadFileBufferState state, final lNumber bytesRead);
		}
		
		public static void readFileBuffer(final File file, final ReadFileBufferCallback callback) {
			try {
				final int BUFFER_SIZE = 1024;
				
				final FileInputStream fis = new FileInputStream(file);
				byte[] buffer = new byte[BUFFER_SIZE]; 
				
				int read = 0;
				lNumber readl = lNumber.NIL;
			    while((read = fis.read(buffer)) > 0){
			    	if (read <= -1) {
			    		break;
			    	}
			    	readl = readl.add(read);
			    	callback.read(buffer, ReadFileBufferState.OK, readl);
			    }
			    
			    callback.read(null, ReadFileBufferState.FINISHED, lNumber.fromInt(-1));
				fis.close();
			}catch (Exception e) {
				e.printStackTrace();
				callback.read(null, ReadFileBufferState.ERROR, lNumber.fromInt(-1));
			}
		}
		
		
		public static String readFileToString(final File file, final Charset charset) throws Exception {
			final ArrayList<String> lines = readFile(file, charset);
			final StringBuilder builder = new StringBuilder();
			for (int i = 0; i < lines.size(); i++) {
				final String str = lines.get(i);
				if (i == lines.size() - 1) {
					builder.append(str);
				}else {
					builder.append(str + "\n");
				}
			}
			
			return builder.toString();
		}

		public static File tempDir() {
			return safeCreate(System.getProperty("java.io.tmpdir"));
		}
		
		public static String readToHex(final File file) throws Exception {
			return ByteUtils.toHEX(read(file));
		}
		
		public static String readToHexCompress(final File file) throws Exception {
			return ByteUtils.toHEX(ZipUtils.zip16(read(file)));
			//EncryptionUtil.byType(Types.ZIP).encrypt(ByteUtils.toHEX(read(file)), "");
		}
		
		public static void writeRaw(final File file, final byte[] data) throws Exception {
			if (!file.exists()) {
				if (!file.createNewFile()) {
					throw new IOException("Cannot create file!");
				}
			}
			final FileOutputStream fos = new FileOutputStream(file);
			fos.write(data);
			fos.flush();
			fos.close();
		}

		public static boolean isType(final File file, final FileType type) {
			final String ext = getFileExtension(file);
			return type.isType(ext);
		}

		public static String getMimeType(final File fileUrl) {
			//System.out.println("file:/" + fileUrl.getPath());
			FileNameMap fileNameMap = URLConnection.getFileNameMap();
			String type = fileNameMap.getContentTypeFor("file:/" + fileUrl.getPath());

			if (type == null) {
				final MimeType t = MimeType.fromFile(fileUrl);
				if (t != MimeType.UNKNOWN) {
					return t.getType();
				}
				return "";
			}
			return type;
		}

		public static String getFileName(String filePath) {
		    if( filePath==null || filePath.length()==0 )
		        return "";
		    filePath = filePath.replaceAll("[/\\\\]+", "/");
		    int len = filePath.length(),
		        upCount = 0;
		    while( len>0 ) {
		        //remove trailing separator
		        if( filePath.charAt(len-1)=='/' ) {
		            len--;
		            if( len==0 )
		                return "";
		        }
		        int lastInd = filePath.lastIndexOf('/', len-1);
		        String fileName = filePath.substring(lastInd+1, len);
		        if( fileName.equals(".") ) {
		            len--;
		        }
		        else if( fileName.equals("..") ) {
		            len -= 2;
		            upCount++;
		        }
		        else {
		            if( upCount==0 )
		                return fileName;
		            upCount--;
		            len -= fileName.length();
		        }
		    }
		    return "";
		}

		public static String getUserLocation() {
			return System.getProperty("user.home");
		}

		public static File createTempFile(final String suff) {
			try {
				final File t = File.createTempFile("_temp_" + MathUtil.randInt(1, Integer.MAX_VALUE), (suff.startsWith(".") ? suff : "." + suff));
				t.deleteOnExit();
				return t;
			}catch (Exception e) {
				e.printStackTrace();
				return createTempFile(suff);
			}
		}

		public static boolean isFile(final String string) {
			try {
				return new File(string).exists();
			}catch (Exception e) {
				return false;
			}
		}

}
